package ace.tool.generate.code.impl.resolver.scheme.resolver.core;

import ace.cmp.util.CaseFormatUtils;
import ace.tool.generate.code.impl.manager.common.EntityModelManager;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.ApiProject;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.SdkProject;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassFieldRemarkMetaInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.EnumClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.EntityClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.resources.model.dto.DaoClassFieldRemarkMetaInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.resources.model.dto.DaoEntityModel;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.resources.model.dto.DaoEntityModelField;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.resources.model.dto.DaoEntityModelFieldEnumInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.dto.ClassFileInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.dto.CommonInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.dto.ProjectInfo;
import ace.tool.generate.code.impl.resolver.scheme.resolver.ClassFileInfoResolver;
import ace.tool.generate.code.impl.service.model.input.GenerateInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelFieldInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelInput;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.WordUtils;

/**
 * @author caspar
 * @date 2023/11/17 10:34
 */
@RequiredArgsConstructor
public class EntityClassInfoResolver {

  private final GenerateInput input;
  private final GenerateModelInput modelInput;
  private final ProjectInfo coreProjectInfo;
  private final CommonInfo commonInfo;
  private final ApiProject apiProject;
  private final SdkProject sdkProject;
  EntityModelManager entityModelManager = new EntityModelManager();

  public EntityClassInfo resolve() {

    String entityModelId = new EntityModelManager().getEntityModelId(input, modelInput);

    ClassFileInfo classFileInfo =
        ClassFileInfoResolver.builder()
            .className(modelInput.getName())
            .classNameSuffix(input.getEntityClassNameSuffix())
            .classPackagePrefix(input.getEntityClassPackagePrefix())
            .rootPackageFullName(commonInfo.getRootPackageFullName())
            .sourceCodeRootDir(coreProjectInfo.getSourceCodeRootDir())
            .build()
            .resolve();
    DtoClassInfo dtoClassInfo = this.entityModelManager.getDtoClassInfoByEntityModelId(entityModelId, this.apiProject.getDtoClasses());

    return EntityClassInfo.builder()
        .info(classFileInfo)
        .entityModelId(entityModelId)
        .model(this.getDaoEntityModel(entityModelId, classFileInfo))
        .dtoClassInfo(dtoClassInfo)
        .build();
  }

  private DaoEntityModel getDaoEntityModel(String entityModelId, ClassFileInfo info) {
    return DaoEntityModel.builder()
        .className(info.getClassName())
        .entityClassNameLowercaseDashName(this.getEntityClassNameLowercaseDashName())
        .entityClassFirstLowercaseClassName(this.getEntityClassFirstLowercaseClassName(info))
        .entityIdType(this.getEntityIdType())
        .entityIdTypeClass(this.getEntityIdTypeClass())
        .packageFullName(info.getClassPackageFullName())
        .remark(modelInput.getRemark())
        .tableName(this.getTableName(info))
        .fields(this.getFields(entityModelId, info))
        .build();
  }

  private String getEntityClassFirstLowercaseClassName(ClassFileInfo info) {
    return WordUtils.uncapitalize(info.getClassName());
  }

  private List<DaoEntityModelField> getFields(String entityModelId, ClassFileInfo info) {
    List<DaoEntityModelField> fields =
        modelInput.getFields().stream()
            .map(p -> this.getField(entityModelId, info, p))
            .collect(Collectors.toList());

    if (this.isNeedAddDeleteFlag(fields)) {
      fields.add(this.getDefaultDeleteField());
    }
    if (input.getEnableCreateTimeField()) {
      fields.add(this.getDefaultCreateTimeField());
    }
    if (input.getEnableUpdateTimeField()) {
      fields.add(this.getDefaultUpdateTimeField());
    }
    if (input.getEnableRowVersionField()) {
      fields.add(this.getDefaultRowVersionField());
    }

    fields.forEach(field -> {
      String columnName = CaseFormatUtils.convertLowerCamelToLowerUnderscore(field.getName());
      String fieldNameUpperUnderscore = columnName.toUpperCase();

      field.setColumnName(columnName);
      field.setFieldNameForStaticColumnName("COLUMN_NAME_" + fieldNameUpperUnderscore);
      field.setFieldNameUpperUnderscore(fieldNameUpperUnderscore);
    });

    fields.forEach(field -> field.setRemarkMeta(this.getRemarkMeta(field)));

    return fields;
  }

  private DaoClassFieldRemarkMetaInfo getRemarkMeta(DaoEntityModelField daoEntityModelField) {
    String entityModelId = this.getEntityModelId();
    String fieldName = daoEntityModelField.getName();

    DtoClassInfo dtoClassInfo = this.entityModelManager.getDtoClassInfoByEntityModelId(entityModelId, this.apiProject.getDtoClasses());

    DtoClassFieldRemarkMetaInfo dtoRemarkMeta = this.entityModelManager.getDtoClassFieldRemarkMetaInfoBy(entityModelId, fieldName, this.apiProject.getDtoClasses());
    DaoClassFieldRemarkMetaInfo daoRemarkMeta = null;
    if (dtoRemarkMeta == null) {
      daoRemarkMeta = DaoClassFieldRemarkMetaInfo.builder()
          .titleFieldName("\"" + daoEntityModelField.getRemark() + "\"")
          .titleFieldContent("")

          .descFieldName("\"" + daoEntityModelField.getRemarkNotes() + "\"")
          .descFieldContent("")
          .build();
    } else {
      String titleFieldName = String.format("%s.%s", dtoClassInfo.getInfo().getClassName(), dtoRemarkMeta.getTitleFieldName());
      String descFieldName = String.format("%s.%s", dtoClassInfo.getInfo().getClassName(), dtoRemarkMeta.getDescFieldName());
      daoRemarkMeta = DaoClassFieldRemarkMetaInfo.builder()
          .titleFieldName(titleFieldName)
          .titleFieldContent("")

          .descFieldName(descFieldName)
          .descFieldContent("")
          .build();
    }

    return daoRemarkMeta;
  }

  private boolean isNeedAddDeleteFlag(List<DaoEntityModelField> fields) {
    boolean isExitsDeleteFlagField =
        fields.stream().anyMatch(p -> p.getName().equals(input.getDeleteFlagFieldName()));
    return input.getEnableDeleteFlagField() && !isExitsDeleteFlagField;
  }

  private DaoEntityModelField getDefaultRowVersionField() {
    return DaoEntityModelField.builder()
        .name(input.getRowVersionFieldName())
        .remark("版本号和乐观锁")
        .remarkNotes("")
        .type(Long.class.getSimpleName())
        .typeClass(Integer.class)
        .deleteFieldFlag(false)
        .rowVersionFieldFlag(true)
        .createTimeFieldFlag(false)
        .updateTimeFieldFlag(false)
        .idFlag(false)
        .nullable(false)
        .stringSizeValid(null)
        .enumInfo(Optional.empty())
        .build();
  }

  private DaoEntityModelField getDefaultUpdateTimeField() {
    return DaoEntityModelField.builder()
        .name(input.getUpdateTimeFieldName())
        .remark("更新时间")
        .remarkNotes("单位-毫秒")
        .type(Long.class.getSimpleName())
        .typeClass(Long.class)
        .deleteFieldFlag(false)
        .rowVersionFieldFlag(false)
        .createTimeFieldFlag(false)
        .updateTimeFieldFlag(true)
        .idFlag(false)
        .nullable(false)
        .stringSizeValid(null)
        .enumInfo(Optional.empty())
        .build();
  }

  private DaoEntityModelField getDefaultCreateTimeField() {
    return DaoEntityModelField.builder()
        .name(input.getCreateTimeFieldName())
        .remark("创建时间")
        .remarkNotes("单位-毫秒")
        .type(Long.class.getSimpleName())
        .typeClass(Long.class)
        .deleteFieldFlag(false)
        .rowVersionFieldFlag(false)
        .createTimeFieldFlag(true)
        .updateTimeFieldFlag(false)
        .idFlag(false)
        .nullable(false)
        .stringSizeValid(null)
        .enumInfo(Optional.empty())
        .build();
  }

  private DaoEntityModelField getDefaultDeleteField() {
    return DaoEntityModelField.builder()
        .name(input.getDeleteFlagFieldName())
        .remark("删除标识")
        .remarkNotes("0-未删除")
        .type(Long.class.getSimpleName())
        .typeClass(Long.class)
        .deleteFieldFlag(true)
        .rowVersionFieldFlag(false)
        .createTimeFieldFlag(false)
        .updateTimeFieldFlag(false)
        .idFlag(false)
        .nullable(false)
        .enumInfo(Optional.empty())
        .stringSizeValid(null)
        .build();
  }

  private DaoEntityModelField getField(
      String entityModelId, ClassFileInfo info, GenerateModelFieldInput modelFieldInput) {
    Optional<EnumClassInfo> enumClassInfoOptional =
        this.entityModelManager.getEnumClassInfoBy(
            modelInput, modelFieldInput, apiProject.getEnumClassInfos());
    Optional<DaoEntityModelFieldEnumInfo> enumInfo =
        this.getDaoEntityModelFieldEnumInfo(modelFieldInput);
    String remarkNotes = this.entityModelManager.getModelFieldEnumRemarkNotes(modelFieldInput);
    String stringSizeValid = this.getStringSizeValid(modelFieldInput);
    String javaComment =
        this.entityModelManager.getModelFieldJavaComment(modelFieldInput, enumClassInfoOptional);
    Boolean deleteFieldFlag =
        Optional.ofNullable(modelFieldInput.getEnableLogicDeleteAnnotation()).orElse(Boolean.FALSE);
    return DaoEntityModelField.builder()
        .name(modelFieldInput.getName())
        .remark(modelFieldInput.getRemark())
        .remarkNotes(remarkNotes)
        .javaComment(javaComment)
        .idFlag(modelFieldInput.getIdFlag())
        .type(modelFieldInput.getType().getSimpleName())
        .typeClass(modelFieldInput.getType())
        .stringSizeValid(stringSizeValid)
        .deleteFieldFlag(deleteFieldFlag)
        .rowVersionFieldFlag(false)
        .createTimeFieldFlag(false)
        .updateTimeFieldFlag(false)
        .nullable(modelFieldInput.getNullable())
        .enumInfo(enumInfo)
        .build();
  }

  private String getStringSizeValid(GenerateModelFieldInput modelFieldInput) {
    return this.entityModelManager.getStringSizeValid(modelFieldInput);
  }

  private Optional<DaoEntityModelFieldEnumInfo> getDaoEntityModelFieldEnumInfo(
      GenerateModelFieldInput modelFieldInput) {

    Optional<EnumClassInfo> enumClassInfoOptional = this.getEnumClassInfo(modelFieldInput);
    if (enumClassInfoOptional.isEmpty()) {
      return Optional.empty();
    }
    return Optional.of(
        DaoEntityModelFieldEnumInfo.builder().info(enumClassInfoOptional.get().getInfo()).build());
  }

  private Optional<EnumClassInfo> getEnumClassInfo(GenerateModelFieldInput modelFieldInput) {
    if (modelFieldInput.getEnums() == null) {
      return Optional.empty();
    }
    String enumId = entityModelManager.getEnumId(modelInput, modelFieldInput);
    EnumClassInfo enumClassInfo =
        entityModelManager
            .getEnumClassInfoByEnumId(this.apiProject.getEnumClassInfos(), enumId)
            .orElse(null);
    return Optional.ofNullable(enumClassInfo);
  }

  private String getTableName(ClassFileInfo info) {
    if (StringUtils.isNotBlank(this.modelInput.getTableName())) {
      return this.modelInput.getTableName();
    }
    return CaseFormatUtils.convertUpperCamelToLowerUnderscore(info.getClassName());
  }

  private String getEntityModelId() {
    String entityModelId = new EntityModelManager().getEntityModelId(input, modelInput);
    return entityModelId;
  }

  private String getEntityIdType() {
    return this.getEntityIdTypeClass().getSimpleName();
  }

  private Class getEntityIdTypeClass() {
    return modelInput.getFields().stream().filter(p -> p.getIdFlag()).findFirst().get().getType();
  }

  private String getEntityClassNameLowercaseDashName() {
    return CaseFormatUtils.convertUpperCamelToLowerUnderscore(modelInput.getName()).replaceAll("_", "-");
  }
}
